package com.luyu.service.impl;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luyu.entity.User;
import com.luyu.entity.UserRole;
import com.luyu.mapper.UserMapper;
import com.luyu.req.UserLoginReq;
import com.luyu.service.PermissionService;
import com.luyu.service.UserRoleService;
import com.luyu.service.UserService;
import com.luyu.temp.TempUserInfo;
import com.luyu.temp.TempUserInfoUtil;
import com.luyu.tenant.TenantContextHolder;
import com.luyu.util.LuAuthenticationManager;
import com.luyu.util.UserContextUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.List;

/**
 * @author HASEE
 * @description 针对表【user】的数据库操作Service实现
 * @createDate 2024-03-11 21:37:12
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserRoleService userRoleService;

    @Resource
    private PermissionService permissionService;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private LuAuthenticationManager luAuthenticationManager;


    @Resource
    private JwtEncoder jwtEncoder;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private TempUserInfoUtil userInfoUtil;

    @Resource
    @Lazy
    private RememberMeServices tokenBasedRememberMeServices;


    @Override
    public void saveRandomUser() {
        User entity = new User();
        entity.setName(RandomUtil.randomNumbers(5));
        entity.setPassword(passwordEncoder.encode("123456"));
        entity.setPhone("123456");
        entity.setEnableFlag(true);
        this.save(entity);
    }

    @Override
    public UserDetails detail(Long id) {
        return this.getById(id);
    }

    @Override
    public List<User> page() {
        Page<User> page = new Page<>();
        return this.page(page).getRecords();
    }


    @Override
    public String loginToken(UserLoginReq req) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(req.getName(), req.getPassword());
        Authentication authenticate = luAuthenticationManager.authenticate(authenticationToken);


        if (ObjUtil.isNotNull(authenticate) && authenticate.isAuthenticated()) {
            JwtClaimsSet claimsSet = JwtClaimsSet
                    .builder()

                    .claims(map -> {
                        // 签发对象主体
                        map.put(JwtClaimNames.SUB, authenticate.getName());
                        // jwi id
                        map.put(JwtClaimNames.JTI, authenticate.getName());
                    })
                    .build();
            JwtEncoderParameters parameters = JwtEncoderParameters.from(claimsSet);
            Jwt jwt = jwtEncoder.encode(parameters);

            String tokenValue = jwt.getTokenValue();
            User principal = (User) authenticate.getPrincipal();
            SecurityContextHolder.getContext().setAuthentication(authenticate);
            TenantContextHolder.setTenantId(principal.getTenantId());

            // 设置用户需要保存的临时信息   用于获取用户需要动态刷新数据，已登录会话管理，rememberMe功能，用户退出jwt失效管理，在线人数统计
            userInfoUtil.setTempUserInfo(TempUserInfo.builder()
                    .userName(req.getName())
                    .currentRoleId(principal.getCurrentRoleId())
                    .tenantId(principal.getTenantId())
                    .build());
            // 后续优化，通过事件发布处理登录成功之后的事情
            if (BooleanUtil.isTrue(req.getRememberMe())) {
                // todo 待优化,增加一个filter,处理是否记住登录状态
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                HttpServletResponse response=((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

                // jdk代理,方法执行失效了
                tokenBasedRememberMeServices.loginSuccess(request,response,authenticate);
            }

            return tokenValue;
        }
        return  null;
    }


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User entity = this.lambdaQuery().eq(User::getName, username).one();
        if (ObjUtil.isNull(entity)) {
            return null;
        }
        List<Long> roleIdList = userRoleService.lambdaQuery()
                .eq(UserRole::getUserId, entity.getId())
                .list()
                .stream()
                .map(UserRole::getRoleId)
                .toList();
        // 控制时间段内用户密码输入多少次错误
        if (redisTemplate.hasKey(username)) {
            Integer errorNum = Integer.valueOf(redisTemplate.opsForValue().get(username).toString());
            if (errorNum > 3) {
                entity.setLocked(true);
            }
        }
        // 设置权限列表
        entity.setAuthorities(permissionService.listByRoleIds(roleIdList));
        // 设置所有角色列表
        entity.setRoleIds(roleIdList);
        entity.setCurrentRoleId(roleIdList.get(0));

        return entity;
    }

    @Override
    public Boolean replaceCurrentRole(Long roleId) {
        List<Long> roleIds = UserContextUtil.getUserAllRoleId();
        if (roleIds.contains(roleId)){
            TempUserInfo tempUserInfo = userInfoUtil.getTempUserInfo();
            tempUserInfo.setCurrentRoleId(roleId);
            userInfoUtil.setTempUserInfo(tempUserInfo);
        }
        return true;
    }

    @Override
    public Boolean kickOut(String userName) {
        // todo 优化点:使用session管理会话精准踢人下线
        return userInfoUtil.remove(userName);
    }


}




